const path = require('path');
const fsp = require('fs/promises');
const { exec } = require('child_process');
const ini = require("ini");

// 模块导入
const { createConfigIni } = require("./src/function");
const { handleCommandLineArgs } = require("./src/command");
const { accessPath, fmtDate, updateProgress, anyKeyExit, confirmAction, returnError } = require("./src/utils");
const { fileMap } = require("./src/fileList"); // 获取 Map

// 根目录判断（支持pkg打包后）
global.ROOT_DIR = process.pkg ? path.dirname(process.execPath) : __dirname;

// 默认配置
const defaultConfig = {
  useNSFW: false,
  CreNewModDir: true,
  folderPrefix: 'sn_',
  onlyFix: false
};

// 缓存数据
const cache = {
  beforeDir: ''
};

let config = {};
const callbacks = [];

let step = 0;
let kill = false;
async function next () {
  step++;
  if (kill) await cleanupAndExit();
}

/**
 * 主程序入口
 */
async function main () {
  try {
    // 1. 安装退出捕获钩子
    await next()
    setupExitHandlers();

    // 2. 检测Node版本
    await next()
    await checkNodeVersion();

    // 3. 加载配置文件
    await next()
    await loadConfiguration();

    // 4. 验证配置
    await next()
    await validateConfiguration();

    // 5. 解析命令行参数
    await next()
    const args = process.argv.slice(2);
    if (await handleCommandLineArgs(args, config.targetDir)) return;

    // 6. 执行前置处理（备份/创建模组目录）
    await next()
    if (!config.onlyFix) await preProcessing();

    // 7. 执行主要文件操作
    await next()
    await performFileOperations();

    // 8. 执行修正回调
    await next()
    await executeCallbacks();

    // 9. 结束等待用户按键退出
    await next()
    await finalExit();
  } catch (error) {
    console.error('\x1b[31m程序发生致命错误:\x1b[0m', error.message);
    await anyKeyExit(1)
  }
}

/**
 * 1. Node版本检测
 */
async function checkNodeVersion () {
  const nodeVersion = process.version;
  const targetVersion = "v18.5.0";

  if (nodeVersion < targetVersion) {
    console.error(`当前 Node.js 版本 ${nodeVersion} 不符合要求，请升级到 \x1b[34m${targetVersion}\x1b[0m 或更高版本`);

    exec('gnvm ls', (error, stdout) => {
      if (error) {
        console.error('gnvm 未安装或未添加到环境变量中');
        console.error('请访问 https://github.com/Kenshin/gnvm 安装 gnvm');
      } else {
        console.log(`已通过 gnvm 检测到当前已安装的 node.js 版本，请使用 \x1b[34mgnvm use [版本号]\x1b[0m 切换至${targetVersion}或以上版本：`);
        console.log('\x1b[32m' + stdout + '\x1b[0m');
      }
    });

    await anyKeyExit(1)
  }
}

/**
 * 2. 加载配置文件
 */
async function loadConfiguration () {
  try {
    const configData = await fsp.readFile(path.resolve(ROOT_DIR, 'config.ini'), 'utf8');
    const fileConfig = ini.parse(configData);
    config = Object.assign({}, defaultConfig, fileConfig);
  } catch (err) {
    await returnError('读取或解析配置文件失败，即将重新生成。稍后请完善 config.ini 中的配置项并保存，然后重新运行程序。', async () => {
      await createConfigIni();
    });
  }
}

/**
 * 3. 验证配置
 */
async function validateConfiguration () {
  if (!config.sourceDir || !config.targetDir) {
    await returnError('config.ini 中的 sourceDir 或 targetDir 配置项缺失，请完善并保存后重启程序。');
  }

  try {
    await fsp.access(config.sourceDir);
  } catch (e) {
    await returnError(`无法访问 sourceDir 配置指定的目录 "${config.sourceDir}"，请检查盘符、拼写是否正确及该路径是否存在。`, e);
  }

  try {
    await fsp.access(config.targetDir);
  } catch (e) {
    await returnError(`无法访问 targetDir 配置指定的目录 "${config.targetDir}"，请检查盘符、拼写是否正确及该路径是否存在。`, e);
  }
}

/**
 * 4. 前置处理 - 创建/备份模组目录
 */
async function preProcessing () {
  if (config.CreNewModDir) {
    const backupBaseDir = path.join(path.dirname(config.targetDir), 'mods-backups');
    const backupDir = path.join(backupBaseDir, fmtDate().getNow12);
    cache.beforeDir = backupDir;
    try {
      // 备份旧目录
      if (await accessPath(config.targetDir)) {
        if (!await accessPath(backupBaseDir)) {
          await fsp.mkdir(backupBaseDir, { recursive: true });
        }
        await fsp.rename(config.targetDir, backupDir, { recursive: true, force: true });
        console.log('\x1b[32m旧的模组目录备份成功\x1b[0m');
      }

      // 创建新目录
      await fsp.mkdir(config.targetDir, { recursive: true });
      console.log('\x1b[32m新目录创建成功\x1b[0m');
    } catch (err) {
      console.error('\x1b[31m模组目录操作失败:\x1b[0m', err);
    }
  } else {
    if (await accessPath(config.targetDir)) {
      try {
        cache.beforeDir = config.targetDir + '-temp';
        if (await accessPath(cache.beforeDir)) await fsp.rm(cache.beforeDir, { recursive: true, force: true });
        await fsp.rename(config.targetDir, cache.beforeDir, { recursive: true, force: true });
        await fsp.mkdir(config.targetDir, { recursive: true });
      } catch (err) {
        console.error('\x1b[31m重建模组目录时失败\x1b[0m', err);
      }
    }
  }
}

/**
 * 5. 文件操作核心逻辑
 */
async function performFileOperations () {
  const numberOfFolders = fileMap.size;
  const numberOfDigits = String(numberOfFolders).length;
  const failed = [];

  const sortedEntries = [...fileMap.entries()]
    .filter(([_, item]) => item && !item.disabled)
    .sort((a, b) => a[1].index - b[1].index);

  for (const [originalName, Item] of sortedEntries) {
    if (kill) break;
    if (!config.useNSFW && Item.r18) continue; // 未开启 R18 内容时，直接跳过
    const suffix = Item.r && typeof Item.r === 'string' ? Item.r : '';
    const newIndex = String(Item.index + 1).padStart(numberOfDigits, '0');
    const newFolderName = `${config.folderPrefix}${newIndex}${suffix}`;
    const oldFolderPath = path.join(config.sourceDir, originalName);
    const newFolderPath = path.join(config.targetDir, newFolderName);

    if (typeof Item.cb === 'function') {
      callbacks.push({ originalName, newFolderName, path: newFolderPath, cb: Item.cb });
    }

    if (config.onlyFix) continue;

    try {
      if (!(await accessPath(oldFolderPath))) {
        updateProgress(`\x1b[31m[${newFolderName}] Error: 文件夹 ${originalName} 不存在\x1b[0m`, false);
        failed.push({ original: originalName, rename: newFolderName });
        continue;
      }

      if (await accessPath(newFolderPath)) {
        await fsp.rm(newFolderPath, { recursive: true, force: true });
        updateProgress(`顺序安装中: \x1b[38;5;240m旧的模组文件夹 ${newFolderName} 已被删除\x1b[0m`);
      }

      await fsp.cp(oldFolderPath, newFolderPath, { recursive: true });
      await fsp.writeFile(path.join(newFolderPath, originalName), '', 'utf8');
      updateProgress(`顺序安装中: \x1b[38;5;8m文件夹 ${originalName} 已成功复制为 ${newFolderName}\x1b[0m`);
    } catch (err) {
      updateProgress(`\x1b[31m复制文件夹 ${originalName} 到 ${newFolderName} 时出错: ${err.message} \x1b[0m`, false);
      failed.push({ original: originalName, rename: newFolderName });
    }
  }

  if (kill) return;

  updateProgress(`顺序安装已完成，共\x1b[34m ${numberOfFolders} \x1b[0m个文件夹，失败\x1b[31m ${failed.length} \x1b[0m个。`, false);
  if (failed.length > 0) console.table(failed);
}

/**
 * 6. 回调执行
 */
async function executeCallbacks () {
  if (callbacks.length === 0) {
    console.log("暂无需要修正的模组");
    return;
  }

  console.log('\x1b[42m开始修正模组文件\x1b[0m');

  for (let i = 0; i < callbacks.length; i++) {
    const logs = await callbacks[i].cb(callbacks[i]);
    console.log(logs);
    // updateProgress(`模组修正完成：${i + 1}/${callbacks.length}`);
  }

  console.log('\x1b[32m模组文件修正完毕\x1b[0m');
}

/**
 * 7. 命令行参数处理
 */


/**
 * 8. 清理与退出
 */
function setupExitHandlers () {
  process.on('SIGINT', handleProcessExit);
  process.on('SIGTERM', handleProcessExit);
  process.on('uncaughtException', handleUncaughtException);
}

async function handleProcessExit (signal) {
  if (step === 7) {
    kill = true
    console.log(`\n收到信号 ${signal}，即将回滚已作出的更改...`);
  }
}

async function handleUncaughtException (err) {
  if (step === 7) {
    kill = true
    console.error('未捕获的异常:', err, '\n即将回滚已作出的更改...');
  }
}

async function cleanupAndExit () {
  if (cache.beforeDir && await accessPath(cache.beforeDir)) {
    try {
      const confirmed = await confirmAction('是否将模组目录回滚至上一版')
      if (confirmed) {
        // 回滚：移除本次处理过的，并将备份版移动至模组目录。整体可以看做无事发生。
        await fsp.rm(config.targetDir, { recursive: true, force: true });
        await fsp.rename(cache.beforeDir, config.targetDir, { recursive: true, force: true });
        console.log('\x1b[32m已滚回上一版，进程结束\x1b[0m');
        await anyKeyExit(0);
      } else {
        // 不回滚：允许了本次处理的行为，保留备份
        console.log('\x1b[32m已完成清理，进程结束\x1b[0m');
        await anyKeyExit(0);
      }
    } catch (err) {
      // 回滚时出错：最好不要发生
      console.error('\x1b[31m版本回滚时出错:\x1b[0m', err);
      console.log("请前往模组目录手动回滚")
      // console.error('请优先尝试关闭所有使用模组目录的进程（包括资源管理器），然后你可以重试。');
      // await waitForAnyKey(null, "按下任意键重试...")
      // cleanupAndExit()
      // await waitForAnyKey();
      await anyKeyExit(0);
    }
  }

}

async function finalExit () {
  console.log("所有操作已完成");
  await anyKeyExit(0);
}

/**
 * 9. 启动主程序
 */
(async function start () {
  await main();
})();
